package com.shop.util.http;

import org.apache.commons.collections.MapUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HttpUtil {

    public static final String RESP_MAP_KEY_HEAD = "header";
    public static final String RESP_MAP_KEY_BODY = "body";
    public static final String RESP_MAP_KEY_STAT = "status";
    public static final String RESP_MAP_KEY_MSG = "message";


    //private static final Logger LOG = LoggerFactory.getLogger(HttpUtil.class);
    private static final CloseableHttpClient defaultHttpClient;
    /**
     * SOCKET_TIMEOUT 数据传输超时时间 默认30秒
     */
    private static final int SOCKET_TIMEOUT = 30000;
    /**
     * CONNECTION_TIMEOUT 建立连接超时时间 默认30秒
     */
    private static final int CONNECTION_TIMEOUT = 30000;
    /**
     * 从httpClient的连接池获取连接的超时时间 ,httpClient默认会一直等待,直到获取连接,这样可能会导致dubbo超时
     * httpUtil设置默认时间为1秒
     */
    private static final int CONNECTION_REQUEST_TIMEOUT = 1000;

    /**
     * 最大连接数,单独使用httpUtil的默认最大连接数,如果同时使用的httpUtil默认的工具方法和自定义的httpClient ,最大连接数会是两者相加
     */
    private static final int MAX_TOTAL = 400;

    /**
     * 每个ip地址对应的最大连接数,假如一个应用只连接到某一个ip地址,最大连接数MAX_TOTAL没有意义,
     * DEFAULT_MAX_PER_ROUTE是最大的连接数
     */
    private static final int DEFAULT_MAX_PER_ROUTE = 200;

    private static RequestConfig defaultRequestConfig = RequestConfig.custom()
            .setSocketTimeout(SOCKET_TIMEOUT)
            .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
            .setConnectTimeout(CONNECTION_TIMEOUT).build();

    private static SSLConnectionSocketFactory sslSocketFactory;

    static {
        try {
            sslSocketFactory = new SSLConnectionSocketFactory(
                    new SSLContextBuilder().loadTrustMaterial(new TrustSelfSignedStrategy())
                            .build());
        } catch (Exception e) {
        }

        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register(HttpConstants.PROTOCOL_HTTP, PlainConnectionSocketFactory.getSocketFactory())
                .register(HttpConstants.PROTOCOL_HTTPS, sslSocketFactory)
                .build();

        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
        cm.setMaxTotal(MAX_TOTAL);
        cm.setDefaultMaxPerRoute(DEFAULT_MAX_PER_ROUTE);
        defaultHttpClient = HttpClients.custom()
                .setConnectionManager(cm)
                //.setSSLSocketFactory(sslSocketFactory)
                //.setSSLHostnameVerifier(new NoopHostnameVerifier())
                .build();
    }

    public static List<BasicNameValuePair> buildBasicNameValuePairListByMap(Map<String, String> paramMap) {
        List<BasicNameValuePair> pairList = new ArrayList<BasicNameValuePair>();
        for (String paramKey : paramMap.keySet()) {
            pairList.add(new BasicNameValuePair(paramKey, paramMap.get(paramKey)));
        }
        return pairList;
    }

    /**
     * 发送get请求
     *
     * @param url     请求行
     * @param headers 请求头
     * @return map
     */
    public static Map<String, Object> get(String url, Map<String, String> headers) {
        return get(url, headers, defaultHttpClient, defaultRequestConfig);
    }

    /**
     * <pre>
     * 发送get请求, 获取原生结果。
     * 适用于那些需要获取输入流的使用场景。
     * 注意：使用完毕一定要执行：<code>response.close()</code>
     *
     * @param url     请求行
     * @param headers 请求头
     * @return CloseableHttpResponse
     * </pre>
     */
    public static CloseableHttpResponse getRawResult(String url, Map<String, String> headers) {
        CloseableHttpResponse response = null;
        try {
            HttpGet get = new HttpGet(url);
            get.setConfig(defaultRequestConfig);
            get.setHeader("Accept-Charset", HttpConstants.UTF_8);
            if (MapUtils.isNotEmpty(headers)) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    get.setHeader(entry.getKey(), entry.getValue());
                }
            }
            response = defaultHttpClient.execute(get);
        } catch (IOException e) {
            //LOG.error("get method , url: {} ", url, e);
        }
        return response;
    }

    /**
     * @param url           请求行
     * @param headers       请求头
     * @param httpClient    httpClient
     * @param requestConfig requestConfig
     * @return
     */
    public static Map<String, Object> get(String url, Map<String, String> headers, CloseableHttpClient httpClient, RequestConfig requestConfig) {
        HashMap<String, Object> result = new HashMap();
        CloseableHttpResponse response = null;
        try {
            HttpGet get = new HttpGet(url);
            get.setConfig(requestConfig);
            get.setHeader(HttpConstants.ACCEPT_CHARSET, HttpConstants.UTF_8);
            if (MapUtils.isNotEmpty(headers)) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    get.setHeader(entry.getKey(), entry.getValue());
                }
            }
            response = httpClient.execute(get);
            buildDefaultMap(result, response);
        } catch (IOException e) {
            //LOG.error("get method , url: {} ", url, e);
            result.put(RESP_MAP_KEY_MSG, "get url : " + url + " ; errorMessage" + e.getMessage());
        } finally {
            try {
                response.close();
            } catch (Exception e) {
            }
        }

        return result;
    }

    private static void buildDefaultMap(Map<String, Object> result, CloseableHttpResponse response) throws IOException {
        result.put(RESP_MAP_KEY_HEAD, response.getAllHeaders());
        HttpEntity httpResponseEntity = response.getEntity();
        String responseBody = EntityUtils.toString(httpResponseEntity, HttpConstants.UTF_8);
        result.put(RESP_MAP_KEY_BODY, responseBody);
        result.put(RESP_MAP_KEY_STAT, response.getStatusLine().getStatusCode());
    }

    /**
     * 返回响应码是否是成功的200
     *
     * @return
     */
    public static boolean isSucc200(Map<String, Object> result) {
        try {
            return result == null ? false : (200 == (int) result.get(RESP_MAP_KEY_STAT));
        } catch (Exception e) {
            return false;
        }
    }

    public static String getBody(Map<String, Object> result) {
        try {
            return result == null ? "" : (String) result.get(RESP_MAP_KEY_BODY);
        } catch (Exception e) {
            return "";
        }
    }

    public static Header[] getHeader(Map<String, Object> result) {
        try {
            return result == null ? null : (Header[]) result.get(RESP_MAP_KEY_HEAD);
        } catch (Exception e) {
            return null;
        }
    }

    public static Map<String, Object> postText(String url, Map<String, String> headers, String content) {
        return postText(url, headers, content, defaultHttpClient, defaultRequestConfig);
    }

    public static Map<String, Object> postText(String url, Map<String, String> headers, String content, CloseableHttpClient httpClient, RequestConfig requestConfig) {
        HashMap<String, Object> result = new HashMap();
        CloseableHttpResponse response = null;
        try {
            HttpPost post = new HttpPost(url);
            post.setConfig(requestConfig);
            post.setHeader(HttpConstants.CONTENT_TYPE, HttpConstants.CONTENT_TYPE_FORM);
            post.setHeader(HttpConstants.ACCEPT_CHARSET, HttpConstants.UTF_8);
            if (MapUtils.isNotEmpty(headers)) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    post.setHeader(entry.getKey(), entry.getValue());
                }
            }
            post.setEntity(HttpEntityUtil.buildTextStringEntity(content));

            response = httpClient.execute(post);

            buildDefaultMap(result, response);

        } catch (IOException e) {
            //LOG.error("postJson , url : {}  , content : {}", url, content, e);
            result.put(RESP_MAP_KEY_MSG, "postText url : " + url + " ; errorMessage" + e.getMessage());
        } finally {
            try {
                response.close();
            } catch (Exception e) {
            }
        }

        return result;
    }

    /**
     * 发送post请求  专门用于使用最频繁的Content-Type 为json的请求
     *
     * @param url     请求行
     * @param headers 请求头 Content-Type 已经设置为json
     * @param content 请求体 json string 格式
     * @return map
     */
    public static Map<String, Object> postJson(String url, Map<String, String> headers, String content) {
        return postJson(url, headers, content, defaultHttpClient, defaultRequestConfig);
    }


    /**
     * @param url           请求行
     * @param headers       请求头
     * @param content       请求体
     * @param httpClient    httpClient
     * @param requestConfig requestConfig
     * @return
     */
    public static Map<String, Object> postJson(String url, Map<String, String> headers, String content, CloseableHttpClient httpClient, RequestConfig requestConfig) {
        HashMap<String, Object> result = new HashMap();
        CloseableHttpResponse response = null;
        try {
            HttpPost post = new HttpPost(url);
            post.setConfig(requestConfig);
            post.setHeader(HttpConstants.CONTENT_TYPE, HttpConstants.CONTENT_TYPE_JSON);
            post.setHeader(HttpConstants.ACCEPT_CHARSET, HttpConstants.UTF_8);
            if (MapUtils.isNotEmpty(headers)) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    post.setHeader(entry.getKey(), entry.getValue());
                }
            }
            post.setEntity(HttpEntityUtil.buildJsonStringEntity(content));

            response = httpClient.execute(post);

            buildDefaultMap(result, response);

        } catch (IOException e) {
            //LOG.error("postJson , url : {}  , content : {}", url, content, e);
            result.put(RESP_MAP_KEY_MSG, "postJson url : " + url + " ; errorMessage" + e.getMessage());
        } finally {
            try {
                response.close();
            } catch (Exception e) {
            }
        }

        return result;
    }

    public static Map<String, Object> postEntity(String url, Map<String, String> headers, List<BasicNameValuePair> pairList) {
        HttpEntity entity = null;
        try {
            entity = new UrlEncodedFormEntity(pairList, HttpConstants.UTF_8);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return postEntity(url, headers, entity, defaultHttpClient, defaultRequestConfig);
    }

    /**
     * 发送post 请求 用于发送各种各样的post请求 该方法并不会根据具体的HttpEntity的具体类型做相应的处理
     * 关于错误日志,该方法无法打印出entity的内容,需要传参之前自己打印日志
     * 文件上传 MultipartEntityBuilder 生产的entity 可以不设置Content-Type 也可以具体设置为 multipart/form-data
     * 需要根据具体情况在
     *
     * @param headers 里面添加具体的Content-Type 不写默认为 text/html
     *                常见的 Content-Type有 application/json ,application/x-www-form-urlencoded ,
     *                application/atom+xml , application/xml , multipart/form-data
     * @param url     请求行
     * @param entity  请求体 HttpEntity 格式
     * @return map key header body
     */
    public static Map<String, Object> postEntity(String url, Map<String, String> headers, HttpEntity entity) {
        return postEntity(url, headers, entity, defaultHttpClient, defaultRequestConfig);
    }

    /**
     * @param url           请求行
     * @param headers       请求头
     * @param entity        请求体
     * @param httpClient    httpClient
     * @param requestConfig requestConfig
     * @return
     */
    public static Map<String, Object> postEntity(String url, Map<String, String> headers, HttpEntity entity, CloseableHttpClient httpClient, RequestConfig requestConfig) {
        HashMap<String, Object> result = new HashMap();
        CloseableHttpResponse response = null;
        try {
            HttpPost post = new HttpPost(url);
            post.setConfig(requestConfig);
            post.setHeader(HttpConstants.ACCEPT_CHARSET, HttpConstants.UTF_8);
            if (MapUtils.isNotEmpty(headers)) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    post.setHeader(entry.getKey(), entry.getValue());
                }
            }
            post.setEntity(entity);

            response = httpClient.execute(post);

            buildDefaultMap(result, response);

        } catch (IOException e) {
            //LOG.error("postEntity method , url : {} ", url, e);
            result.put(RESP_MAP_KEY_MSG, "postEntity url : " + url + " ; errorMessage" + e.getMessage());
        } finally {
            try {
                response.close();
            } catch (Exception e) {
            }
        }

        return result;
    }

    /**
     * 获取自定义的httpClient
     *
     * @param maxTotal    httpclient最大连接数
     * @param maxPerRoute 每个ip地址最大连接数
     * @return httpclient
     */
    public static CloseableHttpClient buildCustomerHttpClient(int maxTotal, int maxPerRoute) {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(maxTotal);
        cm.setDefaultMaxPerRoute(maxPerRoute);
        return HttpClients.custom()
                .setConnectionManager(cm)
                .build();
    }

    /**
     * 获取超时时间设置
     * 单位毫秒
     *
     * @param socketTimeout            数据传输超时时间
     * @param connectionTimeout        建立连接超时时间
     * @param connectionRequestTimeout 从httpClient的连接池获取连接超时时间
     * @return RequestConfig
     */
    public static RequestConfig buildCustomerRequestConfig(int socketTimeout, int connectionTimeout, int connectionRequestTimeout) {
        return RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectionTimeout)
                .setConnectionRequestTimeout(connectionRequestTimeout)
                .build();
    }


    /**
     * @param httpUrl
     * @return 设定文件
     * @return String 返回类型
     * @方法名 httpClent
     * @说明 (传递参数的方法)
     */
    @SuppressWarnings("static-access")
    public static String httpClentString(String httpUrl, String content) {
        try {
            // 创建连接
            URL url = new URL(httpUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setRequestMethod("POST");
            connection.setUseCaches(false);
            connection.setRequestProperty("Accept-Charset", "utf-8");
            connection.setRequestProperty("contentType", "utf-8");
            connection.setInstanceFollowRedirects(true);
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            connection.connect();
            PrintWriter out = new PrintWriter(new OutputStreamWriter(connection.getOutputStream(), "utf-8"));
            out.println(content);
            out.close();

            // 读取响应
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String lines;
            StringBuffer sb = new StringBuffer("");
            while ((lines = reader.readLine()) != null) {
                lines = new String(lines.getBytes(), "utf-8");
                sb.append(lines);
            }
            reader.close();
            // 断开连接
            connection.disconnect();

            return sb.toString();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }


}
